home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / MailManager / Utilities.m < prev    next >
Encoding:
Text File  |  1993-06-25  |  19.7 KB  |  653 lines

  1. /*
  2.  * Program:    Distributed Electronic Mail Manager (Utilities)
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    9 May 1989
  13.  * Last Edited:    25 June 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #import "MailManager.h"
  38.  
  39. // Global variables from preferences panel
  40.  
  41. const char *defaultbcc;        // default bcc list
  42. const char *defaultcc;        // default cc list
  43. const char *hostlist[] = {    // server list for sending
  44.   NIL,                // preferred SMTP server
  45.   NIL,                // domain name (for sending)
  46.   "mailhost","localhost",NIL};    // last chance entries
  47. const char *outbox;        // saved messages file
  48. const char *personalname;    // personal name (for sending)
  49. const char *repository;        // preferred repository
  50. const char *username;        // preferred user name (for sending)
  51. BOOL autoexpunge = NIL;        // automatic expunge when mailbox closed
  52. BOOL autologin = NIL;        // automatic login with previous password
  53. BOOL autoopen = NIL;        // automatic open
  54. BOOL autoread = NIL;        // automatic read when opening mailbox
  55. BOOL autozoom = NIL;        // automatic zoom to new messages
  56. BOOL closefinalkill = NIL;    // close read window on final kill in sequence
  57. BOOL literaldisplay = NIL;    // literal display
  58. BOOL nolinebreaking = NIL;    // don't line break in send windows
  59. BOOL nopersonal = NIL;        // don't show personal names in recipient lists
  60. BOOL readbboards = NIL;        // mdd: enable/disable bboards
  61. BOOL lockedopen = NIL;        // (internal) single locked mailbox windows
  62. BOOL lockedread = NIL;        // (internal) single locked read windows
  63. short debug = NIL;        // debugging state
  64. short format = MBOX;        // default format is mbox
  65. double interval = 0;        // number of seconds between wakeups
  66.  
  67.  
  68. // Global variables
  69.  
  70. char *localhost;        // local host name
  71. char *localuser;        // local user name
  72. char *localpersonal;        // local personal name
  73. char *lastlogin = NIL;        // last login username
  74. Font *defaultfont;        // default font for all text
  75.  
  76. // Build header for message
  77. // Accepts: pointer to text
  78. //        MAIL stream or NIL to return a prototype header
  79. //        message cache element
  80.  
  81. void headerline (char *text,MAILSTREAM *stream,MESSAGECACHE *elt)
  82. {
  83.   char from[FROMLEN];
  84.   char flgs[FLAGSLEN];
  85.   char sub[TMPLEN];
  86.   char date[TMPLEN];
  87.   long i = 999999;
  88.   if (stream && elt) {        // want real header?
  89.                 // yes, do fetchfrom now so elt is set up
  90.     mail_fetchfrom (from,stream,elt->msgno,FROMLEN-1);
  91.                 // compute flag string
  92.     flgs[0] = elt->recent ? (elt->seen ? 'R' : 'N') : (elt->seen ? ' ' : 'U');
  93.     flgs[1] = elt->flagged ? 'F' : ' ';
  94.     flgs[2] = elt->answered ? 'A' : ' ';
  95.     flgs[3] = elt->deleted ? 'D' : ' ';
  96.     flgs[4] = '\0';
  97.     mail_date (date,elt);    // set date
  98.     if (i = elt->user_flags) {    // first stash user flags into subject
  99.       sub[0] = '{';        // open brace for keywords
  100.       sub[1] = '\0';        // tie off so strcat starts off right
  101.                 // output each keyword
  102.       do strcat (sub,stream->user_flags[find_rightmost_bit (&i)]);
  103.       while (i && strcat (sub," "));
  104.       strcat (sub,"} ");    // close brace and space before subject text
  105.     }
  106.     else sub[0] = '\0';        // no user flags in front of subject
  107.                 // append the subject
  108.     mail_fetchsubject (sub + strlen (sub),stream,elt->msgno,SUBJECTLEN-1);
  109.     sub[SUBJECTLEN-1] = '\0';    // truncate as necessary
  110.     i = elt->rfc822_size;    // get message size
  111.   }
  112.   else {            // want prototype text
  113.     strcpy (date,DATE);        // dummy date
  114.     strcpy (from,FROM);        // dummy from
  115.     strcpy (flgs,FLAGS);    // dummy flags
  116.     strcpy (sub,SUBJECT);    // dummy subject
  117.   }    
  118.                 // output what we got
  119.   sprintf (text,"%s%.6s %s %s (%ld chars)",flgs,date,from,sub,i);
  120. }
  121.  
  122. // Append a selection string
  123. // Accepts: string to append to
  124. //        selection name
  125. //        selection argument
  126.  
  127. void selstr (char *str,char *name,const char *arg)
  128. {
  129.   char tmp[TMPLEN];
  130.   char c;
  131.   char *s = (char *) arg;
  132.   strcat (str,name);        // append the start of the string
  133.                 // do it this way if need to be literal
  134.   while (c = *s++) if (c == '"' || c == '\015' || c == '\012') {
  135.     sprintf (tmp," {%lu}\015\012",strlen (arg));
  136.     strcat (str,tmp);
  137.     strcat (str,arg);
  138.     return;
  139.   }
  140.   strcat (str," \"");        // else output as quoted string
  141.   strcat (str,arg);
  142.   strcat (str,"\"");
  143.   return;
  144. }
  145.  
  146. // Address list management
  147.  
  148.  
  149. // Copy address list for reply
  150. // Accepts: MAP address list
  151. //          optional MTP address list to append to
  152. // Returns: MTP address list
  153.  
  154.  
  155. ADDRESS *copy_adr (ADDRESS *adr,ADDRESS *ret)
  156. {
  157.   char tmp[TMPLEN],mymbx[TMPLEN],mydom[TMPLEN];
  158.   char *s;
  159.   ADDRESS *dadr,*prev;
  160.   if (!adr) return ret;        // no-op if empty list
  161.                 // make uppercase copy of my mailbox name
  162.   ucase (strcpy (mymbx,username));
  163.                 // and uppercase copy of my domain name
  164.   ucase (strcpy (mydom,domainname));
  165.                 // run down previous list until the end
  166.   if (prev = ret) while (prev->next) prev = prev->next;
  167.                 // don't copy if it matches me
  168.     do if (adr->host && (strcmp (mymbx,ucase (strcpy (tmp,adr->mailbox))) ||
  169.              (strcmp (mydom,ucase (strcpy (tmp,adr->host))) &&
  170.               (s = strchr (tmp,'.')) && strcmp (mydom,s+1)))) {
  171.       dadr = mail_newaddr ();    // instantiate new address
  172.       if (!ret) ret = dadr;    // set return if new address list
  173.                 // tie on to the end of any previous
  174.       if (prev) prev->next = dadr;
  175.       dadr->personal = cpystr (adr->personal);
  176.       dadr->adl = cpystr (adr->adl);
  177.       dadr->mailbox = cpystr (adr->mailbox);
  178.       dadr->host = cpystr (adr->host);
  179.       dadr->error = NIL;    // initially no error
  180.       dadr->next = NIL;        // no next pointer yet
  181.       prev = dadr;        // this is now the previous
  182.   } while (adr = adr->next);    // go to next address in list
  183.   return (ret);            // return the MTP address list
  184. }
  185.  
  186. // C Client event notification
  187.  
  188.  
  189. // Message matches a search
  190. // Accepts: MAIL stream
  191. //        message number
  192.  
  193. void mm_searched (MAILSTREAM *stream,long msgno)
  194. {
  195.   [(getstreamprop (stream))->window searched:msgno];
  196. }
  197.  
  198.  
  199. // Message exists (i.e. there are that many mesages in the mailbox)
  200. // Accepts: MAIL stream
  201. //        message number
  202.  
  203. void mm_exists (MAILSTREAM *stream,long msgno)
  204. {
  205.   STREAMPROP *s = getstreamprop (stream);
  206.   s->nmsgs = msgno;        // note number of messages
  207.   [s->window exists:msgno];    // let us know about the message
  208. }
  209.  
  210.  
  211. // Message expunged
  212. // Accepts: MAIL stream
  213. //        message number
  214.  
  215. void mm_expunged (MAILSTREAM *stream,long msgno)
  216. {
  217.   [(getstreamprop (stream))->window expunged:msgno];
  218. }
  219.  
  220.  
  221. // Notification event
  222. // Accepts: MAIL stream
  223. //        string to log
  224. //        error flag
  225.  
  226. void mm_notify (MAILSTREAM *stream,char *string,long errflg)
  227. {
  228.   mm_log (string,errflg);    // not doing anything special yet
  229. }
  230.  
  231. // Mailbox found
  232. // Accepts: mailbox name
  233.  
  234. void mm_mailbox (char *string)
  235. {
  236.                 // add this to the browser
  237.   [NXApp addMailboxToBrowser:string];
  238. }
  239.  
  240.  
  241. // BBoard found
  242. // Accepts: BBoard name
  243.  
  244. void mm_bboard (char *string)
  245. {
  246. //char tmp[MAILTMPLEN];
  247. //sprintf (tmp,"*%s",string);    // need something better than this
  248. //[NXApp addMailboxToBrowser:tmp];
  249. }
  250.  
  251. // Log an event for the user to see
  252. // Accepts: string to log
  253. //        error flag
  254.  
  255. void mm_log (char *string,long errflg)
  256. {
  257.   char *tmp;
  258.   struct tm *t;
  259.   struct timeb tb;
  260.   switch (errflg) {
  261.   case NIL:            // no error
  262.   case PARSE:            // parse glitch
  263.   case WARN:            // warning only
  264.     tmp = fs_get (16+strlen (string));
  265.     ftime (&tb);        // get time and timezone poop
  266.     t = localtime (&tb.time);    // convert to individual items
  267.     sprintf (tmp,"%02d:%02d %s",t->tm_hour,t->tm_min,string);
  268.     [NXApp telemetry:tmp];
  269.     fs_give ((void **) &tmp);
  270.     break;
  271.   case ERROR:            // error that causes a punt
  272.   default:
  273.     NXRunAlertPanel ("Error",string,NIL,NIL,NIL);
  274.     break;
  275.   }
  276. }
  277.  
  278.  
  279. // Log an event to debugging telemetry
  280. // Accepts: string to log
  281.  
  282. void mm_dlog (char *string)
  283. {
  284.   char *tmp;
  285.   struct tm *t;
  286.   struct timeb tb;
  287.   tmp = fs_get (16+strlen (string));
  288.   ftime (&tb);            // get time and timezone poop
  289.   t = localtime (&tb.time);    // convert to individual items
  290.   sprintf (tmp,"%02d:%02d:%02d %s",t->tm_hour,t->tm_min,t->tm_sec,string);
  291.   [NXApp telemetry:tmp];
  292.   fs_give ((void **) &tmp);
  293. }
  294.  
  295. // Get user name and password for this host
  296. // Accepts: host name
  297. //        where to return user name
  298. //        where to return password
  299. //        trial number of this login
  300.  
  301. void mm_login (char *host,char *username,char *password,long trial)
  302. {
  303.                 // do the login
  304.   [NXApp login:host user:username password:password force:trial];
  305. }
  306.  
  307.  
  308. // Notification that the c-client has gone critical
  309. // Accepts: MAIL stream
  310.  
  311. void mm_critical (MAILSTREAM *stream)
  312. {
  313. }
  314.  
  315.  
  316. // Notification that the c-client has released critical
  317. // Accepts: MAIL stream
  318.  
  319. void mm_nocritical (MAILSTREAM *stream)
  320. {
  321. }
  322.  
  323. // Notification of a disk write error in the c-client
  324. // Accepts: MAIL stream
  325. //        system error code
  326. //        seriousness flag
  327. // Returns: abort flag
  328.  
  329. long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
  330. {
  331.   char tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
  332.   sprintf (tmp,"Error writing %s",stream->mailbox);
  333.   sprintf (tmpx,"%s%s -- continuing will retry",strerror (errno),
  334.        serious ? " -- mailbox possibly damaged" : "");
  335.   NXRunAlertPanel (tmp,tmpx,NIL,NIL,NIL);
  336.   return NIL;
  337. }
  338.  
  339.  
  340. // Log a fatal event to telemetry
  341. // Accepts: string to log
  342.  
  343. void mm_fatal (char *string)
  344. {
  345.   mm_log (string,ERROR);
  346. }
  347.  
  348. // Periodic routines
  349.  
  350.  
  351. // Periodic wake-up call
  352. // Accepts: timed entry identifier
  353. //        current time
  354. //        data received when timed event set up
  355.  
  356. DPSTimedEntryProc mm_wakeup (DPSTimedEntry teNumber,double now,void *userData)
  357. {
  358.                 // ping with a no-op
  359.   [(MBoxWindow *) userData noop:NIL];
  360.   return NIL;
  361. }
  362.  
  363. // Streamprop simulator for MBoxwindow to stream backpointers
  364.  
  365.  
  366. //  This isn't really the best way of doing this, but I seriously doubt there
  367. // are going to be that many simultaneous MBoxwindow's and this way doesn't
  368. // involve having to hack into the C client.  Note that the nomenclature is a
  369. // bit funny; the "window" stored on the streamprop is the MBoxWindow object
  370. // that you can send messages to and not an actual window.
  371. //
  372. //  We also store the number of messages on the streamprop, because when the
  373. // mailbox is selected on a new Open Mailbox there is no MBoxWindow to stash
  374. // it on.  The MBoxWindow isn't made until the mailbox is completely open.
  375. //
  376. //  For the same reason, getstreamprop () creates a streamprop with a null
  377. // MBoxWindow if there is no streamprop registered, whereas putstreamprop ()
  378. // will return an error if no streamprop exists yet.
  379.  
  380.  
  381. // List of stream properties
  382. STREAMPROP *streamproplist = NIL;
  383.  
  384.  
  385. // Register MBoxWindow for stream
  386. // Accepts: stream (streamprop for it must already exist)
  387. //        MBoxWindow or NIL to kill the streamprop
  388.  
  389. void putstreamprop (MAILSTREAM *stream,MBoxWindow *window)
  390. {
  391.   STREAMPROP *s = streamproplist;
  392.   while (s) {            // run through the list
  393.     if (s->stream == stream) {    // found it yet?
  394.                 // yes, register MBoxWindow if there
  395.       if (window) s->window = window;
  396.       else {            // killing the streamprop
  397.                 // bind any successor streamprops to prior
  398.     if (s->previous) s->previous->next = s->next;
  399.                 // if no prior, successors is new list
  400.     else streamproplist = s->next;
  401.                 // bind prior to successors
  402.     if (s->next) s->next->previous = s->previous;
  403.     fs_give ((void **) &s);    // flush this streamprop
  404.       }
  405.       return;            // all done
  406.     }
  407.     else s = s->next;        // run down the list
  408.   }
  409.                 // lost the streamprop?
  410.   fatal ("Attempt to register MBoxWindow on unknown MAIL stream"); 
  411. }
  412.  
  413. // Return streamprop for stream
  414. // Accepts: MAIL stream
  415. // Returns: streamprop (created if it doesn't exist)
  416.  
  417. STREAMPROP *getstreamprop (MAILSTREAM *stream)
  418. {
  419.   STREAMPROP *s = streamproplist;
  420.   while (s) {            // run down the stream list
  421.                 // return the streamprop when we find it
  422.     if (s->stream == stream) return s;
  423.     else s = s->next;
  424.   }
  425.                 // not found, make a new streamprop
  426.   s = (STREAMPROP *) fs_get (sizeof (STREAMPROP));
  427.   s->stream = stream;        // bind the stream
  428.   s->window = NIL;        // no MBoxWindow yet
  429.   s->nmsgs = 0;            // number of messages not set yet
  430.   s->previous = NIL;        // this is the front of the list
  431.                 // append other streamprops if any
  432.   if (s->next = streamproplist) s->next->previous = s;
  433.   return (streamproplist = s);    // and put us on the head of the list
  434. }
  435.  
  436. // Fix new lines (change CRLF to \n)
  437. // Accepts: character string
  438. // Returns: same string with returns squeezed out
  439.  
  440. char *fixnl (char *text)
  441. {
  442.   int i,j;
  443.   if (text) {            // we're a no-op if no text
  444.                 // squeeze out any CR's
  445.     for (i = j = 0; text[i] != '\0'; i++)
  446.       if (text[i] != '\015' || text[i+1] != '\012') text [j++] = text[i];
  447.     text[j] = '\0';        // tie off end of string
  448.   }
  449.   return (text);
  450. }
  451.  
  452.  
  453. // Get text from view, breaking lines and changing \n to CRLF
  454. // Accepts: view
  455. // Returns: text string or NIL
  456.  
  457. unsigned char *gettext (Text *view)
  458. {
  459.   unsigned char *s,*d;
  460.   unsigned char *ret = NIL;
  461.   int i = [view textLength];
  462.   if (i++) {            // any text at all?
  463.                 // get enough space for the text in worst case
  464.     d = ret = (unsigned char *) fs_get (i * 2);
  465.                 // get text from the view
  466.     [view getSubstring:(char *) (s = ret + i) start:0 length:i];
  467.     if (nolinebreaking) {    // non line breaking case is easy
  468.       while (*s) {        // copy string, inserting CR's before LF's
  469.                 // if CR, copy it and skip LF check
  470.         if (*s == '\015') *d++ = *s++;
  471.                 // else if LF, insert a CR before it
  472.         else if (*s == '\012') *d++ = '\015';
  473.         if (*s) *d++ = *s++;    // copy (may be null if last char was CR)
  474.       }
  475.       if (d[-1] != '\012') {    // force ending with a newline
  476.     *d++ = '\015';
  477.     *d++ = '\012';
  478.       }
  479.       *d = '\0';        // tie off destination
  480.     }
  481.     else linebreak (d,s);    // line breaking case
  482.   }
  483.   return (ret);
  484. }
  485.  
  486. // Copy text, breaking lines
  487. // Accepts: destination pointer
  488. //        source pointer
  489.  
  490. void linebreak (unsigned char *dst,unsigned char *src)
  491. {
  492.   unsigned char *s = src;
  493.   unsigned char *b = NIL;
  494.   unsigned char *l = dst;
  495.   int i = 0;            // init character position
  496.   while (*s) switch (*s) {    // until source runs out
  497.   case '\015':            // return
  498.   case '\012':            // line feed
  499.     wordcopy (&b,&dst,&src,s);    // copy any word
  500.     if (*s == '\015') *dst++ = *s++;
  501.     else *dst++ = '\015';    // write CR before bare LF
  502.     if (*s == '\012') *dst++ = *s++;
  503.     i = 0;            // clear position count
  504.     src = s;            // source starts on next character
  505.     l = dst;            // note new line
  506.     break;
  507.   case '\t':            // tab
  508.     i |= 7;            // move to in front of stop, fall into space
  509.   case ' ':            // space
  510.     wordcopy (&b,&dst,&src,s);    // copy word if any
  511.                 // count line, increment pointer
  512.     if (++i <= MAXLINELENGTH) s++;
  513.     else {            // line will overflow, write new line
  514.       *dst++ = '\015'; *dst++ = '\012'; i = 0;
  515.                 // skip to end of whitespace
  516.       while (*s == ' ' || *s == '\t') s++;
  517.       src = s;            // update source to end of whitespace
  518.       l = dst;            // note new line
  519.     }
  520.     break;
  521.   default:            // some other (presumably printing)
  522.     if (!b) b = s;        // remember start of word
  523.     if (++i > MAXLINELENGTH) {    // count character, see if overflow
  524.                 // if line empty must break up the word
  525.       if (l == dst) wordcopy (&b,&dst,&src,s);
  526.       else {            // word goes on next line
  527.     *dst++ = '\015'; *dst++ = '\012'; i = 0;
  528.     src = s = b;        // start new line on current word
  529.     l = dst;        // note new line
  530.       }
  531.     }
  532.                 // no overflow, possibly break on hyphen
  533.     else if (*s++ == '-') wordcopy (&b,&dst,&src,s);
  534.     break;
  535.   }
  536.   wordcopy (&b,&dst,&src,s);    // copy word if one still remains
  537.   if (dst[-1] != '\012') {    // force ending with a newline
  538.     *dst++ = '\015';
  539.     *dst++ = '\012';
  540.   }
  541.   *dst = '\0';            // tie off destination
  542. }
  543.  
  544. // Copy word - helper routine for linebreak ()
  545. // Accepts: pointer to start of word
  546. //        pointer to destination pointer
  547. //        pointer to start of copy region (may include leading whitespace)
  548. //        end of copy region
  549.  
  550. void wordcopy (unsigned char **wrd,unsigned char **dst,unsigned char **src,
  551.            unsigned char *end)
  552. {
  553.   int i;
  554.   if (*wrd) {            // only do this if saw a word
  555.     memcpy (*dst,*src,(i = end - *src));
  556.     *wrd = NIL;            // no longer a pending word
  557.     *src += i;            // update source
  558.     *dst += i;            // update destination
  559.   }
  560. }
  561.  
  562. // Append message to local mailbox
  563. // Accepts: file to append to
  564. //        address block of sender
  565. //        elt holding message date
  566. //        message header text
  567. //        message text
  568.  
  569. void append_msg (FILE *file,ADDRESS *s,MESSAGECACHE *elt,char *hdr,
  570.          unsigned char *text)
  571. {
  572.   char tmp[MAILTMPLEN];
  573.   unsigned char c;
  574.   struct tm d;
  575.   unsigned char *m = (unsigned char *) fs_get ((hdr ? strlen (hdr) : 0) + 2 +
  576.                            (text ? strlen ((char *) text) :
  577.                         0));
  578.   unsigned char *t = m;
  579.                 // copy header and text
  580.   if (hdr) while (c = *hdr++) if (c != '\r') *t++ = c;
  581.   if (text) while (c = *text++) if (c != '\r') *t++ = c;
  582.   *t = '\0';            // tie off string
  583.   switch (format) {
  584.   case MBOX:            // Berkeley header
  585.     if (s) sprintf (tmp,"%s@%s",s->mailbox,s->host ? s->host : localhost);
  586.     else strcpy (tmp,"somebody");
  587.     d.tm_sec = elt->seconds; d.tm_min = elt->minutes; d.tm_hour = elt->hours;
  588.     d.tm_mday = elt->day; d.tm_mon = elt->month - 1;
  589.     d.tm_year = elt->year + (BASEYEAR - 1900);
  590.     d.tm_gmtoff = 0;        // timezone not used
  591.     mktime (&d);        // get day of the week
  592.     fprintf (file,"From %s %s",tmp,asctime (&d));
  593.     t = m;            // start of message
  594.     while (c = *t++) {        // for each character of message
  595.       fputc (c,file);        // copy character, insert broket if needed
  596.       if (c == '\n' && t[0] == 'F' && t[1] == 'r' && t[2] == 'o' && t[3] == 'm'
  597.       && t[4] ==' ') fputc ('>',file);
  598.     }
  599.     fputc ('\n',file);        // must have a final newline
  600.     break;
  601.   case MTXT:            // Tenex header
  602.     fprintf (file,"%s,%ld;000000000000\n",mail_date (tmp,elt),t - m);
  603.     fputs ((char *) m,file);    // blat the text
  604.     break;
  605.   default:            // something badly broken
  606.     fatal ("Unknown mailbox format in append_msg!");
  607.     break;
  608.   }
  609.   fs_give ((void **) &m);    // flush the scratch text
  610. }
  611.  
  612. // Output filtered header
  613. // Accepts: envelope
  614. // Returns: string of filtered header
  615.  
  616. char *filtered_header (ENVELOPE *env)
  617. {
  618.   char tmp[64*MAILTMPLEN];
  619.   char *s = tmp;
  620.   if (env->date) {
  621.     sprintf (s,"Date: %s\n",env->date);
  622.     s += strlen (s);
  623.   }
  624.   else *s = '\0';        // start with empty string
  625.   write_address (&s,"From",env->from);
  626.   if (env->subject) {
  627.     sprintf (s,"Subject: %s\n",env->subject);
  628.     s += strlen (s);
  629.   }
  630.   write_address (&s,"To",env->to);
  631.   write_address (&s,"cc",env->cc);
  632.   write_address (&s,"bcc",env->bcc);
  633.   strcat (s,"\n");        // delimit with new line
  634.   return cpystr (tmp);
  635. }
  636.  
  637.  
  638. // Write address list
  639. // Accepts: destination pointer
  640. //        name of this header
  641. //        address list
  642.  
  643. void write_address (char **dest,char *tag,ADDRESS *adr)
  644. {
  645.   char c;
  646.   char *s = *dest;
  647.   char *t = *dest;
  648.   rfc822_address_line (dest,tag,NIL,adr);
  649.   do if ((c = *s++) != '\r') *t++ = c;
  650.   while (c);            // filter out newlines
  651.   *dest = --t;
  652. }
  653.